﻿using System;
using System.Reflection;
using System.Text;
using System.IO;
using System.Xml;
using System.Threading;

namespace Ecopsy.DataTier
{
    public delegate void AddChild(object obj);
    public class EntitySingle:Entity,ICloneable 
    {        
        
        public EntitySingle ():base () 
        {
            if (ImSupprotType (this.GetType().GetInterfaces(),typeof(ISqlSignature)))            
                _RW_Engine =new SQL_RW_SingleEng (this);
                else
                _RW_Engine = new RW_SingleEng(this);

            if (ImSupprotType(this.GetType().GetInterfaces(), typeof(IFileSignature)))
                _RW_Engine = new File_RW_SingleEng(this);
        }
        
        private string _ID = Guid.NewGuid().ToString();
        
        public virtual string ID { get { return _ID; } set { _ID = value; } }

        public override   void Save()
        {
            _RW_Engine.Save();
            SetHash();
        }

        [EntityNonSerialized]
        public virtual bool IsCacheObject
        {
            get { return true; }


        }

        static string _AsynParam_ImAsXml = string.Empty;
        static string _AsynParam_ConnectionString = string.Empty;
        private static EntitySingle _AsynParam_Entity = null;

        /// <summary>
        /// Асинхонное соханение объекта
        /// </summary>
        /// <param name="ConnectionString"></param>
        public override void Save(string ConnectionString)
        {
            _AsynParam_Entity = this;
            lock (_AsynParam_ConnectionString)
            {
                lock (_AsynParam_Entity)
                {
                    _AsynParam_ConnectionString = ConnectionString;                          
                    Thread t = new Thread(new ThreadStart(AsynFastSave));
                    t.Start();
                }
            }
        }

        private static void AsynFastSave()
        {
            EntitySingle obj = _AsynParam_Entity;            
            SecuretyContext SC = new SecuretyContext(_AsynParam_ConnectionString);             
            
            SqlTools tls = new SqlTools(SC);
            ISqlSignature o = (ISqlSignature)obj;
            string sp = obj.State.Equals(EntityState.Deleted) ? o.DeleteProcedure : o.SaveProcedure;
            tls.ExecuteCommand_NonQuery(sp, obj);            
        }

        public override void Load()
        {
            _RW_Engine.Load ();
            SetHash();

        }

        /// <summary>
        /// Cерилизованный объект. Граф сокращенный - Объекты ISqlObject не серилизуются. 
        /// Свойство несерилизуемое.
        /// </summary>
        [EntityNonSerialized]
        virtual public string ImAsXML
        {
            get
            {
                return ToXml(false);
            }

        }
       
        /// <summary>
        /// Краткая запись бизнес объекта
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="ObjectName"></param>
        public  override void WriteShortXml(System.Xml.XmlWriter writer, string ObjectName)
        {
            
            Type MyType = this.GetType();
            PropertyInfo[] MyFields = MyType.GetProperties();

            // начало узла
            writer.WriteStartElement(ObjectName);
            //// Обязательно пишем тип и сборку 
            writer.WriteAttributeString(SerilizeContext .TypePref, MyType.ToString());
            writer.WriteAttributeString(SerilizeContext.AssemblyPref, MyType.Assembly.ToString());
            writer.WriteAttributeString("ID", this.ID);
            writer.WriteEndElement();

        }

        public override void WriteXml(System.Xml.XmlWriter writer, string ObjectName, bool Full)
        {
            try
            {
                
  
                Type MyType = this.GetType();                
                // начало узла
                writer.WriteStartElement(ObjectName);
                //// Обязательно пишем тип и сборку 
                writer.WriteAttributeString(SerilizeContext.TypePref, MyType.ToString());
                writer.WriteAttributeString(SerilizeContext.AssemblyPref, MyType.Assembly.ToString());
                                
                Type t = MyGetType(MyType.Assembly.ToString(), MyType.ToString());

                string sValue = "";
                foreach (PropertyInfo inf in AttPropertys)
                    {
                        object v = inf.GetValue(this, null);
                        if (null == v) continue;
                        sValue = v.ToString();                        
                        if (String.IsNullOrEmpty(v.ToString())) continue;
                        writer.WriteAttributeString(inf.Name, sValue);
                    }


                ////// пишем функции расширения 
                //if (this.ReportTypeExt != string.Empty)
                //{
                //    Type tp = ReflectionItem.MyGetType(this.ReportTypeExt);
                //    if (null != tp)
                //    {
                //        foreach (MemberInfo inf in tp.GetMethods())
                //        {
                //            object[] atr = inf.GetCustomAttributes(typeof(ImReportItem), false);
                //            if (atr.Length == 0) continue;
                //            object[] p = new object[1];
                //            p[0] = this;
                //            object r = ((MethodInfo)inf).Invoke(this, p);
                //            string tagname = ((ImReportItem)atr[0]).TagName;
                //            writer.WriteAttributeString((tagname.Length == 0 ? r.ToString() : tagname), r.ToString());                            
                //            continue;
                //        }
                //    }
                //}

                ////пишем перечисления
                foreach (PropertyInfo inf in IEnum)
                {
                    object v = inf.GetValue(this, null);
                    if (null == v) continue;
                    if (String.IsNullOrEmpty(v.ToString())) continue;
                    writer.WriteAttributeString(inf.Name, v.ToString());                    
                }
                
                // пишем объекты
                foreach (PropertyInfo inf in ISerilzeProperties)
                {
                    object v = inf.GetValue(this, null);
                    if (null == v) continue;                  
                    
                    ISerializableItem I = (ISerializableItem)v;
                    if (Full)
                    {
                        I.WriteXml(writer, inf.Name, Full);
                        continue;
                    }

                    //// вариант для Full- false
                    if (v is ISqlSignature)
                        I.WriteShortXml(writer, inf.Name);
                    else
                        I.WriteXml(writer, inf.Name, Full);

                    continue;
                }
                
                writer.WriteEndElement();
            }
            catch( Exception e)
            {
                string s = e.Message;
                throw;
            }
        }


        /// <summary>
        /// Серелизуются все элементарные типы, из entity  пишуться только те чо имеют признак ReportItem
        /// Пустые значения пишутся. 
        /// Если не указан ObjectName = пишется TagName объекта
        /// </summary>
        /// <param name="writer"></param>
        public override void WriteReportXml(System.Xml.XmlWriter writer,string ObjectName)
        {
            Type MyType = this.GetType();
            // начало узла
            writer.WriteStartElement(ObjectName .Length==0?TagName :ObjectName );

            // элементарные типы
            foreach (PropertyInfo   inf in ReflectionItem.GetSerisableAttProperty(this.GetType () ))
            {
                                
                object v = inf.GetValue(this, null);
                if (null == v) { writer.WriteElementString(inf.Name, ""); continue;}                
                writer.WriteElementString(inf.Name, v.ToString());                 
            }

            // Отчетные Entity
            foreach (PropertyInfo inf in ReflectionItem.GetPropertyList(this.GetType(), "ISerializableItem", typeof(ImReportItem)))
            {   
                object v = inf.GetValue(this, null);
                if (null == v) { writer.WriteElementString(inf.Name, ""); continue; }
                ((ISerializableItem)v).WriteReportXml(writer, inf.Name);              
            }

            ////функции расширения 
            Type  tp= ReflectionItem.MyGetType(this.ReportTypeExt);
            if (null != tp)
            {
                foreach (MemberInfo  inf in tp.GetMethods())
                {
                    object [] atr= inf.GetCustomAttributes(typeof(ImReportItem), false);  
                    if (atr.Length ==0) continue ;

                    //if (inf.IsDefined(typeof(ImReportItem), true))                    
                    {

                         object []p= new  object[1] ;
                        p[0]=this ;
                        object r=((MethodInfo)inf).Invoke(this, p);
                        string tagname=((ImReportItem)atr[0]).TagName;
                        writer.WriteElementString((tagname.Length ==0?r.ToString ():tagname ),r.ToString ()); 
                        continue; 
                    }
                }
            }

            // entity типы 
            writer.WriteEndElement();
        }

        /// <summary>
        /// Получить отчетный XML
        /// </summary>
        /// <returns></returns>
        public string GetReportXML()
        {
            StringBuilder sb;
            MemoryStream ms = new MemoryStream();
            sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb, System.Globalization.CultureInfo.InvariantCulture);
            XmlTextWriter w = new XmlTextWriter(sw);
            w.Formatting = Formatting.Indented;
            w.Indentation = 3;
            this.WriteReportXml(w, TagName);
            return sb.ToString();

        }

        #region ICloneable Members

        public object Clone()
        {

            string s = this.ToXml(false);
            EntitySingle obj = (EntitySingle)ReflectionItem.CreateInstance(s);
            obj.ReadXml(s);
            return obj;
        }

        #endregion
    }
}
